#include "common.h"

#include "../mapobjects/objectstorage.h"
#include "../mapobjects/mapobject.h"
#include "../mapobjects/geometry.h"
#include "mainwindow.h"
#include "usertool.h"

#include <QtGui/QtGui>
#include <QtGui/QMenuBar>

static const double factor = 1.3;

Viewer::Viewer(QWidget *parent) : QWidget(parent), storage_(NULL), offset(0, 0)
{
}

Viewer::~Viewer()
{
}

QMatrix Viewer::getMap()
{
    if (storage_)
    {    
        QRectF drawingBounds = storage_ -> boundingRect ();
        QMatrix toScreen;
        toScreen.translate( -drawingBounds.topLeft().x() * scale, 
						    -drawingBounds.topLeft().y() * scale );
        toScreen.translate(offset.x(), offset.y());
        toScreen.scale ( scale, scale );
        return toScreen.inverted ();
    }
    return QMatrix();
}

void Viewer::setStorage( ObjectStorage *storage )
{
    if ( storage_ )
    {
        delete storage_;
    }
    storage_ = storage;

    QSize sz = this->size();
    QRectF bounds = storage_->boundingRect();
    scale = std::min ( sz.width()  / ( bounds.bottomRight().x() - bounds.topLeft().x ()), 
                       sz.height() / ( bounds.bottomRight().y() - bounds.topLeft().y ()));
    offset.setX(0);
    offset.setY(0);

    adjustSize();

    emit mapLoaded();
}

QSize Viewer::sizeHint () const
{
    return QSize();
}

void Viewer::scrollTo(QPointF from, QPointF to)
{
    offset -= from - to;
    if (storage_)
    {   
        double maxw = storage_->boundingRect().width() * scale;
        double maxh = storage_->boundingRect().height() * scale;
        double w = size().width();
        double h = size().height();        
    }
    update();
}

void Viewer::zoom(QPoint stablePoint, double zoomFactor)
{
    QPointF world(stablePoint);
    world = viewToLogic(world);

    scale *= zoomFactor;
    world = logicToView(world);
    
    scrollTo(world.toPoint(), stablePoint);
}

void Viewer::pointSelectEvent(const QPointF &pt)
{
	emit pointClicked(viewToLogic(pt));
}

void Viewer::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    QBrush brush ( Qt::white );
    QRectF qRect = event->rect();
    painter.fillRect ( qRect, brush );

    if ( storage_ )
    {
        QMatrix toScreen = getMap().inverted();
        painter.setMatrix(toScreen);

        qRect = getMap().mapRect(qRect).normalized();        
        std::vector<PrimitiveBase *> primitives;
		storage_ -> getPrimitives ( qRect, primitives );

        for ( std::vector<PrimitiveBase *>::iterator it = primitives.begin();
            it != primitives.end(); ++it )
        {            
            PrimitiveBase *primitive = *it;
            QRectF bounds = primitive->boundingRect();

            if ( rectArea ( primitive -> boundingRect ()) > 1e-8 * rectArea ( qRect ))
            {
                primitive->draw ( &painter );
            }            
        }

		for ( QSet<const MapObject *>::const_iterator it = cosmetics.begin(); 
			  it != cosmetics.end(); ++it )
		{		
			const MapObject *cosmetic = *it;
			cosmetic->drawPrimitives(&painter);
		}
    }
}

void Viewer::addCosmetics(const MapObject *cosmetic)
{
	cosmetics.insert(cosmetic);
}

void Viewer::removeCosmetics(const MapObject *cosmetic)
{
	cosmetics.remove(cosmetic);
}

MainWindow::MainWindow()
{
    setObjectName("MapViewer");
    setWindowTitle("Map Viewer");

    QMenu *menu = menuBar()->addMenu(tr("&File"));
    menu->addAction(tr("&Open"), this, SLOT(loadStorage()));
    menu->addAction(tr("&Quit"), this, SLOT(close()));

    QMenu *tools = menuBar()->addMenu(tr("&Tools"));
    QMenu *zoom = tools->addMenu(tr("&Zoom"));    
    zoom->addAction(tr("Zoom &in"), this, SLOT(zoomIn()));
    zoom->addAction(tr("Zoom o&ut"), this, SLOT(zoomOut()));
    tools->addAction(tr("&Drag"), this, SLOT(drag()));
	tools->addAction(tr("&Select"), this, SLOT(select()));
	tools->addAction(tr("&Search"), this, SLOT(search()));

    viewer = new Viewer(this);
    setCentralWidget(viewer);

    tool = NULL;
}

MainWindow::~MainWindow()
{
}

void MainWindow::loadStorage()
{
    QString qsStorageFile = QFileDialog::getOpenFileName(this, "Choose a database", "", "Map databases (*.mp)");
    if (!qsStorageFile.isEmpty())
    {    
		ObjectStorage *storage = MapLoader::Load ( std::string ( qsStorageFile.toAscii().data ()));
        viewer->setStorage(storage);
    }

    viewer->adjustSize();
}

void MainWindow::zoomIn()
{
    setTool(new ZoomTool(factor));
}

void MainWindow::zoomOut()
{
    setTool(new ZoomTool(1 / factor));
}

void MainWindow::drag()
{
    setTool(new DragTool());
}

void MainWindow::setTool(UserTool *newTool)
{
    if (tool)
    {
        tool->uninstallTool(viewer);
        delete tool;
    }
    tool = newTool;
	if (tool)
	{
		tool->installTool(viewer);
	}
}

Viewer *MainWindow::getViewer()
{
    return viewer;
}

void MainWindow::select()
{
	setTool(new SelectTool());
}

void MainWindow::search ()
{
	setTool(new SearchTool ());
	emit searchToolSelected ();
}